<!DOCTYPE html>
<html lang="zh">
<head>
    <title>Robotic Process Automation Tests</title>
    <meta charset="UTF-8">
    <meta content="Robotic Process Automation" name="description">
    <meta content="RPA" name="description">
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <link href="https://github.githubassets.com/favicons/favicon.png" rel="shortcut icon" type="image/x-icon"/>
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@6.x/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
</head>
<body>
<v-app id="app">
    <v-app-bar app color="white" flat>
        <v-container class="py-0 fill-height">
            <v-avatar class="mr-10" size="48">
                <v-icon color="grey darken-4">mdi-robot</v-icon>
            </v-avatar>
            <v-btn v-for="link in links" text :href="link.url">
                {{ link.name }}
            </v-btn>
            <v-spacer></v-spacer>
            <v-menu bottom right>
                <template v-slot:activator="{ on, attrs }">
                    <v-btn text
                           class="align-self-center mr-4"
                           v-bind="attrs"
                           v-on="on">
                        <v-icon>mdi-translate</v-icon>
                    </v-btn>
                </template>
                <v-list>
                    <v-list-item v-for="locale in lang.locales" @click="changeLocale(locale.code)">
                        {{ locale.name }}
                    </v-list-item>
                </v-list>
            </v-menu>
        </v-container>
    </v-app-bar>

    <v-main class="grey lighten-3">
        <v-container>
            <v-sheet rounded="lg">
                <v-dialog v-model="dialog" persistent scrollable max-width="500px">
                    <v-card>
                        <v-card-title class="text-h5">
                            Select App
                        </v-card-title>
                        <v-divider></v-divider>
                        <v-card-text>
                            <v-list>
                                <v-list-item-group v-model="appId" color="grey darken-4">
                                    <v-list-item v-for="app in apps.list" :value="app.id">
                                        <v-list-item-content>
                                            <v-list-item-title v-text="app.name"></v-list-item-title>
                                        </v-list-item-content>
                                    </v-list-item>
                                </v-list-item-group>
                            </v-list>
                        </v-card-text>
                        <v-divider></v-divider>
                        <v-card-actions>
                            <v-spacer></v-spacer>
                            <v-btn text @click="selectApp()">
                                OK
                            </v-btn>
                        </v-card-actions>
                    </v-card>
                </v-dialog>
                <v-card>
                    <v-card-text>
                        <v-data-table
                                :headers="user.headers" :items="users.list"
                                :options.sync="user.options" :server-items-length="users.total"
                                :loading="users.loading" loading-text="Loading..."
                                @update:options="listUsers()">
                            <template v-slot:top>
                                <v-toolbar flat>
                                    <v-row>
                                        <v-col>
                                            <v-text-field
                                                    v-model="user.params.ids"
                                                    label="User Id"
                                                    clearable dense>
                                            </v-text-field>
                                        </v-col>
                                        <v-col>
                                            <v-text-field
                                                    v-model="user.params.account"
                                                    label="Account"
                                                    clearable dense>
                                            </v-text-field>
                                        </v-col>
                                        <v-col>
                                            <v-text-field
                                                    v-model="user.params.nickname"
                                                    label="Nickname"
                                                    clearable dense>
                                            </v-text-field>
                                        </v-col>
                                        <v-col>
                                            <v-text-field
                                                    v-model="user.params.realname"
                                                    label="Realname"
                                                    clearable dense>
                                            </v-text-field>
                                        </v-col>
                                        <v-col>
                                            <v-text-field
                                                    v-model="user.params.company"
                                                    label="Company"
                                                    clearable dense>
                                            </v-text-field>
                                        </v-col>
                                        <v-col>
                                            <v-btn @click="listUsers()">Search</v-btn>
                                        </v-col>
                                    </v-row>
                                    <v-dialog v-model="task.dialog" max-width="600px">
                                        <v-card>
                                            <v-card-title>
                                                <span class="text-h5">New Task</span>
                                            </v-card-title>
                                            <v-card-text>
                                                <v-form v-model="task.valid">
                                                    <v-container>
                                                        <v-row>
                                                            <v-col cols="12" md="12">
                                                                <v-select
                                                                        v-model="task.params.type"
                                                                        :rules="task.rules.type"
                                                                        :items="taskTypes[task.app.id]"
                                                                        item-value="key"
                                                                        item-text="value"
                                                                        label="Type *"
                                                                        required>
                                                                </v-select>
                                                            </v-col>
                                                            <v-col cols="12" md="12">
                                                                <v-dialog v-model="task.menu" width="290px">
                                                                    <template v-slot:activator="{ on, attrs }">
                                                                        <v-text-field
                                                                                v-model="task.params.scheduleTime"
                                                                                :rules="task.rules.scheduleTime"
                                                                                label="Schedule Time"
                                                                                v-bind="attrs" v-on="on"
                                                                                optional clearable></v-text-field>
                                                                    </template>
                                                                    <v-date-picker
                                                                            v-if="task.datePicker"
                                                                            v-model="task.temp.scheduleDate"
                                                                            scrollable>
                                                                        <v-spacer></v-spacer>
                                                                        <v-btn color="primary" text
                                                                               :disabled="!task.temp.scheduleDate"
                                                                               @click="task.datePicker=false; task.timePicker=true">
                                                                            OK
                                                                        </v-btn>
                                                                    </v-date-picker>
                                                                    <v-time-picker
                                                                            v-if="task.timePicker"
                                                                            v-model="task.temp.scheduleTime"
                                                                            format="24hr" use-seconds scrollable>
                                                                        <v-spacer></v-spacer>
                                                                        <v-btn color="primary" text
                                                                               :disabled="!task.temp.scheduleTime"
                                                                               @click="task.datePicker=true; task.timePicker=false; task.menu=false; task.params.scheduleTime=task.temp.scheduleDate +' '+ task.temp.scheduleTime">
                                                                            OK
                                                                        </v-btn>
                                                                    </v-time-picker>
                                                                </v-dialog>
                                                            </v-col>
                                                            <v-col cols="12" md="12">
                                                                <v-textarea
                                                                        v-model="task.params.data"
                                                                        :rules="task.rules.data"
                                                                        label="Data"
                                                                        rows="3"
                                                                        auto-grow
                                                                        optional>
                                                                </v-textarea>
                                                            </v-col>
                                                        </v-row>
                                                    </v-container>
                                                </v-form>
                                            </v-card-text>
                                            <v-card-actions>
                                                <v-spacer></v-spacer>
                                                <v-btn text @click="task.dialog=false">
                                                    Close
                                                </v-btn>
                                                <v-btn text :disabled="!task.valid" @click="task.dialog=false; createTask()">
                                                    Execute
                                                </v-btn>
                                            </v-card-actions>
                                        </v-card>
                                    </v-dialog>
                                </v-toolbar>
                            </template>
                            <template v-slot:item.avatar="{ item }">
                                <v-avatar size="25">
                                    <v-img :src="item.avatar" :alt="item.nickname"></v-img>
                                </v-avatar>
                            </template>
                            <template v-slot:item.status="{ item }">
                                {{ enums['UserStatus'][item.status] }}
                            </template>
                            <template v-slot:item.actions="{ item }">
                                <v-icon @click.stop="task.dialog=true; task.user=item; task.app=app">
                                    mdi-robot-angry
                                </v-icon>
                            </template>
                        </v-data-table>
                    </v-card-text>
                </v-card>
                <v-dialog v-model="task.result.dialog" persistent max-width="500px">
                    <v-card :loading="task.result.waiting">
                        <v-card-title>
                            <span class="text-h5">{{ task.result.waiting ? 'Waiting' : 'Result' }}</span>
                        </v-card-title>
                        <v-card-text align="center">
                            <v-skeleton-loader
                                    v-if="task.result.waiting"
                                    width="280px"
                                    height="280px"
                                    type="card">
                            </v-skeleton-loader>
                            <div v-if="!task.result.waiting">
                                <v-img
                                        v-if="task.result.qrcode"
                                        :src="task.result.qrcode"
                                        max-width="280px"
                                        max-height="280px">
                                </v-img>
                                <p class="text-h5 text--primary" v-if="enums['TaskStatus']">
                                    {{ enums['TaskStatus'][task.result.status] }}
                                </p>
                                <p class="text--primary" v-if="task.result.message">
                                    {{ task.result.message }}
                                </p>
                                <p class="text--primary">
                                    {{ task.result.result }}
                                </p>
                            </div>
                        </v-card-text>
                        <v-card-actions>
                            <v-spacer></v-spacer>
                            <v-btn text @click="task.result.dialog=false; closeTaskResult()">
                                Close
                            </v-btn>
                        </v-card-actions>
                    </v-card>
                </v-dialog>
            </v-sheet>
        </v-container>
    </v-main>

    <v-snackbar v-model="snackbar">
        {{ message }}
        <template v-slot:action="{ attrs }">
            <v-btn color="pink" text v-bind="attrs" @click="snackbar = false">
                Close
            </v-btn>
        </template>
    </v-snackbar>
</v-app>
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.x/dist/axios.min.js"></script>
<script src="scripts/qrcode.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        vuetify: new Vuetify(),
        data: {
            lang: {
                locales: [
                    {code: 'zhHans', name: '简体中文'},
                    {code: 'en', name: 'English'}],
                current: 'zhHans',
            },
            links: [
                {
                    name: "Source Code",
                    url: "https://github.com/yihleego/robotic-process-automation"
                },
                {
                    name: "Documentation",
                    url: "https://github.com/yihleego/robotic-process-automation/README.md"
                },
                {
                    name: "Issues",
                    url: "https://github.com/yihleego/robotic-process-automation/issues"
                },
            ],
            dialog: false,
            snackbar: false,
            message: "",
            apps: {
                list: [],
                total: 0,
            },
            users: {
                list: [],
                total: 0,
            },
            enums: {},
            constants: {},
            taskTypes: {},
            app: null,
            appId: null,
            user: {
                headers: [
                    {text: 'User Id', value: 'id', sortable: true, align: 'start'},
                    {text: 'Avatar', value: 'avatar', sortable: false},
                    {text: 'Account', value: 'account', sortable: true},
                    {text: 'Nickname', value: 'nickname', sortable: false},
                    {text: 'Realname', value: 'realname', sortable: false},
                    {text: 'Company', value: 'company', sortable: false},
                    {text: 'Phone', value: 'phone', sortable: false},
                    {text: 'Status', value: 'status', sortable: false},
                    {text: 'Created Time', value: 'createdTime', sortable: true},
                    {text: 'Updated Time', value: 'updatedTime', sortable: true},
                    {text: 'Actions', value: 'actions', sortable: false, align: 'center'},
                ],
                params: {},
                options: {
                    itemsPerPage: 10,
                    multiSort: false,
                    mustSort: false,
                    page: 1,
                    sortBy: ['createdTime'],
                    sortDesc: [true],
                },
            },
            task: {
                app: {},
                user: {},
                task: {},
                dialog: false,
                valid: false,
                menu: false,
                datePicker: true,
                timePicker: false,
                params: {},
                temp: {},
                rules: {
                    type: [v => !!v || 'Please select a type'],
                    scheduleTime: [v => isDateTime(v) || 'Invalid schedule time'],
                    data: [v => isJSON(v) || 'Invalid data'],
                },
                result: {
                    dialog: false,
                    waiting: false,
                    qrcode: null,
                    message: null,
                    result: null,
                    job: null,
                },
            },
        },
        methods: {
            init() {
                this.listConstants()
                this.listTaskTypes()
                this.listApps()
            },
            changeLocale(lang) {
                this.$vuetify.lang.current = lang
            },
            listConstants() {
                axios.get(`/constants`)
                  .then((response) => {
                      let result = response.data
                      if (!result.success) {
                          this.toast(result.message)
                          return
                      }
                      this.constants = result.data
                      for (let key in this.constants) {
                          let values = this.constants[key]
                          let map = {}
                          for (let i in values) {
                              map[values[i].key] = values[i].value
                          }
                          this.enums[key] = map
                      }
                  })
                  .catch((error) => {
                      this.toast(error)
                  })
            },
            listTaskTypes() {
                axios.get(`/tasks/types`)
                  .then((response) => {
                      let result = response.data
                      if (!result.success) {
                          this.toast(result.message)
                          return
                      }
                      this.taskTypes = result.data
                      let map = {}
                      for (let appId in this.taskTypes) {
                          let values = this.taskTypes[appId]
                          for (let i in values) {
                              if (!map[appId]) {
                                  map[appId] = {}
                              }
                              map[appId][values[i].key] = values[i].value
                          }
                          this.enums['TaskType'] = map
                      }
                  })
                  .catch((error) => {
                      this.toast(error)
                  })
            },
            listApps() {
                let params = {
                    page: 1,
                    size: 1000,
                    sort: 'createdTime:ASC',
                }
                axios.get(`/apps`, {params: params})
                  .then((response) => {
                      let result = response.data
                      if (!result.success) {
                          this.toast(result.message)
                          return
                      }
                      this.apps = result.data
                      this.dialog = true
                  })
                  .catch((error) => {
                      this.toast(error)
                  })
            },
            listUsers() {
                if (!this.app) {
                    return
                }
                this.users.loading = true
                let params = this.getParams(this.user.params, this.user.options)
                params.appIds = this.app.id
                this.sort
                axios.get(`/users`, {params: params})
                  .then((response) => {
                      let result = response.data
                      if (!result.success) {
                          this.toast(result.message)
                          return
                      }
                      this.users = result.data
                  })
                  .catch((error) => {
                      this.toast(error)
                  })
            },
            createTask() {
                this.task.params.appId = this.task.app.id
                this.task.params.userId = this.task.user.id
                axios.post(`/tasks`, {tasks: [this.task.params]})
                  .then((response) => {
                      let result = response.data
                      if (!result.success) {
                          this.toast(result.message)
                          return
                      }
                      let task = result.data[0]
                      this.task.task = task
                      this.task.params = {}
                      this.showTaskResult(task.id)
                  })
                  .catch((error) => {
                      this.toast(error)
                  })
            },
            showTaskResult(taskId) {
                this.task.result.dialog = true
                this.task.result.waiting = true
                this.task.result.qrcode = null
                this.task.result.retries = 0
                this.task.result.job = setInterval(() => {
                    axios.get(`/tasks/${taskId}`)
                      .then((response) => {
                          let result = response.data
                          if (!result.success) {
                              this.toast(result.message)
                              clearInterval(this.task.result.job)
                              return
                          }
                          this.task.result.retries++
                          let task = result.data
                          let taskType = task.type
                          let taskStatus = task.status
                          let taskMessage = task.message
                          let taskResult = task.result
                          if (taskStatus === 0 || taskStatus === 1) {
                              // Created, Running
                              console.log('Waiting', task);
                              if (this.task.result.retries > 100) {
                                  this.task.result.waiting = false
                                  this.task.result.message = 'Timeout'
                                  clearInterval(this.task.result.job)
                              }
                              return
                          }
                          if (taskStatus === 2 || taskStatus === 3) {
                              // Deleted, Cancelled
                              this.toast('Task has been cancelled')
                          } else if (taskStatus === 10) {
                              this.task.result.success = true
                          } else {
                              this.task.result.success = false
                              this.toast('Failed to obtain QR code ' + taskMessage)
                          }
                          this.task.result.waiting = false
                          this.task.result.status = taskStatus
                          this.task.result.message = taskMessage
                          this.task.result.result = null
                          if (taskResult && isJSON(taskResult)) {
                              let r = JSON.parse(taskResult);
                              this.task.result.result = r
                              if (taskType === 'login' && r.qrcode) {
                                  this.task.result.qrcode = createQRCode(r.qrcode, {size: 256})
                              }
                          }
                          clearInterval(this.task.result.job)
                      })
                      .catch((error) => {
                          this.toast(error)
                          clearInterval(this.task.result.job)
                      })
                }, 1000)
            },
            closeTaskResult() {
                if (this.task.result.job) {
                    clearInterval(this.task.result.job)
                }
                this.task.result.waiting = false
                this.task.result.success = false
            },
            selectApp() {
                console.log(this.appId)
                if (!this.appId) {
                    return
                }
                for (let i in this.apps.list) {
                    if (this.apps.list[i].id === this.appId) {
                        this.app = this.apps.list[i]
                        break
                    }
                }
                this.dialog = false
                this.listUsers()
            },
            getParams(params, options) {
                params.page = options.page
                params.size = options.itemsPerPage
                let sortProperties = options.sortBy
                let sortDirections = options.sortDesc
                if (sortProperties && sortDirections) {
                    let orders = []
                    for (let i in sortProperties) {
                        if (sortDirections[i]) {
                            orders.push(sortProperties[i] + ":desc")
                        } else {
                            orders.push(sortProperties[i] + ":asc")
                        }
                    }
                    params.sort = orders.join(",")
                }
                return this.trimParams(params)
            },
            trimParams(obj) {
                Object.keys(obj)
                  .forEach(i => {
                      if (obj[i] == null || obj[i] === '' || i.startsWith('_')) {
                          delete obj[i]
                      }
                  })
                return obj
            },
            toast(message) {
                console.error(message)
                this.message = message
                this.snackbar = true
            },
        },
        created() {
            this.init()
        },
    })

    function isDateTime(v) {
        if (!v) {
            return true
        }
        let reg = /^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/
        let regExp = new RegExp(reg)
        return regExp.test(v)
    }

    function isJSON(v) {
        if (!v) {
            return true
        }
        try {
            JSON.parse(v)
            return true
        } catch (e) {
            return false
        }
    }

    function createQRCode(text, options) {
        options = options || {}
        let typeNumber = options.typeNumber || 0;
        let errorCorrectionLevel = options.errorCorrectionLevel || 'M';
        let size = options.size || 500;
        let mode = options.mode || 'Byte';
        let mb = options.mb || 'UTF-8';
        qrcode.stringToBytes = qrcode.stringToBytesFuncs[mb];
        let qr = qrcode(typeNumber, errorCorrectionLevel);
        qr.addData(text, mode);
        qr.make();
        let moduleCount = qr.getModuleCount();
        let cellSize = size / moduleCount
        let margin = (size - moduleCount * cellSize) / 2
        return qr.createDataURL(cellSize, margin, size);
    }
</script>
</body>
</html>


